package com.xuecheng.auth.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;


@EnableWebSecurity
//@Secured 注解可以用于在方法上定义角色权限。
//@PreAuthorize 和 @PostAuthorize 注解的支持，这两个注解可以用于在方法级别进行更细粒度的安全控制，例如在方法调用之前或之后进行权限验证。
@EnableGlobalMethodSecurity(securedEnabled = true,prePostEnabled = true)
//这个重写的是WebSecurityConfigurerAdapter  认证服务重写的是AuthorizationServerConfigurerAdapter
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    //配置认证管理bean 在 Spring Security 中，AuthenticationManager 负责对用户进行认证，验证用户的身份和凭据是否有效。
    //通过定义这个 bean，可以让 Spring 容器知道在需要进行认证时应该使用哪个认证管理器。通常情况下，我们会在配置中使用这个 bean 来处理用户认证的相关逻辑。
    @Bean
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    //配置用户信息服务
//    @Bean
//    public UserDetailsService userDetailsService() {
//        //这里配置用户信息,这里暂时使用这种方式将用户存储在内存中
//        InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
//        manager.createUser(User.withUsername("zhangsan").password("123").authorities("p1").build());
//        manager.createUser(User.withUsername("lisi").password("456").authorities("p2").build());
//        return manager;
//    }

    //这个就是Spring Security 中的authenticationManager委托的daoAuthenticationProvider进行对用户信息的认证 具有查询数据库的功能 所以认证服务需要配置数据库的连接
    //因为重写了daoAuthenticationProvider所以要在Spring Security中进行注入
    @Autowired
    DaoAuthenticationProviderCustom daoAuthenticationProviderCustom;

    //这个就是让AuthenticationManager委托daoAuthenticationProvider进行认证
    //因为重写了daoAuthenticationProvider所以要在Spring Security中进行注入 然后进行声明重写了daoAuthenticationProviderCustom
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.authenticationProvider(daoAuthenticationProviderCustom);
    }


    //数据库中的密码加过密的，用户输入的密码是明文，我们需要修改密码格式器PasswordEncoder
    // 在daoAuthenticationProviderCustom进行密码对比的时候使用的是passwordEncoder进行的对比
    @Bean
    public PasswordEncoder passwordEncoder() {
//        return NoOpPasswordEncoder.getInstance();
        //BCryptPasswordEncoder，它是将用户输入的密码编码为BCrypt格式与数据库中的密码进行比对 因为数据库的密码是经过加密的 所以用户输入的密码也需要进行加密对比
        return new BCryptPasswordEncoder();
    }

    //Spring Security的配置安全拦截机制
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .authorizeRequests()
                .antMatchers("/r/**").authenticated()//访问/r开始的请求需要认证通过
                .anyRequest().permitAll()//其它请求全部放行
                .and()
                .formLogin().successForwardUrl("/login-success");//登录成功跳转到/login-success
        http.logout().logoutUrl("/logout");//退出地址
    }

    //测试BCryptPasswordEncoder的加密方式 也就是为什么用户输入的密码能和数据库里面加密过后的密码进行比较  在后面PasswordAuthServiceImpl需要自定义这个方法进行密码的对比
    //通过这种方式可以把用户输入的明文密码和数据库里面加密过后的密码进行匹配
    public static void main(String[] args) {
        String password = "111111";
        PasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
        for(int i=0;i<10;i++) {
            //每个计算出的Hash值都不一样  passwordEncoder.encode(password)根据原生密码生成一个密码 原始密码没有边 生成的密码一直在变当时可以匹配成功
            String hashPass = passwordEncoder.encode(password);
            System.out.println(hashPass);
            //虽然每次计算的密码Hash值不一样但是校验是通过的
            boolean f = passwordEncoder.matches(password, hashPass);
            System.out.println(f);
        }
    }


}
